home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 14642 / 14642.xpi / chrome / modules / tracker.js < prev   
Text File  |  2009-10-20  |  22KB  |  599 lines

  1. /* Copyright 2009, Boomtango.com.  All Rights Reserved. */
  2. /* tracker.js
  3.  * Responsible for tracking user behavior and sending to db
  4.  */
  5.  
  6. var EXTERNAL_TRACKERS = ['shopping', 'wikipedia', 'search', 'bookmark', 'contact', 'video'];
  7. var EXPORTED_SYMBOLS = ["boomtangoTracker"];
  8.  
  9. function CustomCategory(name, keywords, regex) {
  10.     this.name = name;
  11.     this.name_plural = name;
  12.     this.keywords = keywords;
  13.     this.regex = regex;
  14.     this.color = boomtangoTracker.app.generateColor();
  15.     this.id = "custom." + encodeURIComponent(name);
  16.     this.description = boomtangoTracker.app.getString("tracker.custom", keywords);
  17. }
  18. CustomCategory.prototype = {
  19.     onLoadTracks: function(url, doc, referrer, content){
  20.         var results = [];
  21.         if(content && content.length){
  22.             if(this.regex){
  23.                 if(content.match(this.keywords)){
  24.                     results.push({
  25.                         type: this.id
  26.                     });
  27.                 }
  28.             } else {
  29.                 var a = this.keywords.split(',');
  30.                 var len = a.length;
  31.                 for(var x = 0; x < len; x++){
  32.                     if(content.indexOf(a[x]) >= 0){
  33.                         results.push({
  34.                             type: this.id
  35.                         });
  36.                         break;
  37.                     }
  38.                 }
  39.             }
  40.         }
  41.         return results;
  42.     }
  43. };
  44. var boomtangoTracker = {
  45.     cc: Components.classes,
  46.     ci: Components.interfaces,
  47.     types: {
  48.         web: { 
  49.             name: "Website", 
  50.             name_plural: "Websites", 
  51.             color: "#289f65",
  52.             isInternal: true,
  53.             preview: function(doc, node, data){
  54.                var nopreview = true;
  55.                if(data.preview && data.preview.length){
  56.                     var src = this.app.getThumb(parseInt(data.preview));
  57.                     if(src.length){ 
  58.                         var image = doc.createElement('image');
  59.                         image.setAttribute("src",  src);
  60.                         image.className = "webthumb";
  61.                         node.appendChild(image);
  62.                         nopreview = false;
  63.                     }
  64.                 } 
  65.                if(nopreview){
  66.                     var vbox = doc.createElement('vbox');
  67.                     var spacer = doc.createElement('spacer');
  68.                     spacer.setAttribute('flex', '100');
  69.                     spacer.setAttribute('height', '80');
  70.                     vbox.appendChild(spacer);
  71.  
  72.                     vbox.className = "nothumb";
  73.                     vbox.setAttribute("style", "width: 240px");
  74.                     var label = doc.createElement('label');
  75.                     label.setAttribute('value', this.app.getString('history.nopreview'));
  76.                     label.className = 'nopreview';
  77.                     vbox.appendChild(label);
  78.                     spacer = doc.createElement('spacer');
  79.                     spacer.setAttribute('flex', '100');
  80.                     spacer.setAttribute('height', '80');
  81.                     vbox.appendChild(spacer);
  82.                     node.appendChild(vbox);
  83.                 }
  84.             }
  85.         }
  86.     },
  87.     loadCategoryTrackers: function(){
  88.         var list = this.app.getCustomCategoryList();
  89.         var len = list.length;
  90.         for(var x=0; x < len; x++){
  91.             var item = list[x];
  92.             this.createCategoryTracker(item.name, item.keywords, item.regex, true);
  93.         }
  94.     },
  95.     loadCustomTrackers: function(){
  96.         var file = Components.classes["@mozilla.org/file/directory_service;1"]
  97.                              .getService(Components.interfaces.nsIProperties)
  98.                              .get("ProfD", Components.interfaces.nsIFile);
  99.         file.append("boomtango");
  100.         file.append("trackers");
  101.  
  102.         var entries = file.directoryEntries;
  103.         var jsregex =  /^(.*)\.bct$/;
  104.         var loader = Components.classes
  105.                 ["@mozilla.org/moz/jssubscript-loader;1"]
  106.                 .getService(Components.interfaces.mozIJSSubScriptLoader);
  107.         var ios = Components.classes["@mozilla.org/network/io-service;1"].
  108.                             getService(Components.interfaces.nsIIOService);
  109.         while(entries.hasMoreElements()){
  110.             var entry = entries.getNext();
  111.             var jsfile = entry.QueryInterface(Components.interfaces.nsIFile);
  112.             var results = jsregex.exec(jsfile.leafName);
  113.             if(results){
  114.                 this.app.log("tracker::loadCustom (" + jsfile.leafName + ")");
  115.                 var jsurl =  ios.newFileURI(jsfile).spec;
  116.                 try {
  117.                     var newtype = {};
  118.                     loader.loadSubScript(jsurl, newtype);
  119.                     if(newtype.module){
  120.                         var id = "import." + results[1];
  121.                         this.types[id] = newtype.module;
  122.                         this.types[id].id = id;
  123.  
  124.                         this.addMemberFuncs(this.types[id]);
  125.                         if(newtype.module.init){
  126.                             newtype.module.init(this.app.getTrackerData(id));
  127.                         }
  128.                     }
  129.                 } catch(e){
  130.                     this.app.log("error loading custom js: " + e.message);
  131.                 }
  132.             }
  133.         }
  134.     },
  135.     enableTracker: function(type, doEnable){
  136.         if(this.app.getTrackerEnabled(type) != doEnable){
  137.             this.app.setTrackerEnabled(type, doEnable);
  138.             
  139.             this.observer.notifyObservers(null, "boomtango-tracker-" + (doEnable ? "add" : "remove"), type);
  140.         }
  141.     },
  142.     createCategoryTracker: function(name, keywords, regex, noAppUpdate){
  143.         this.app.log("tracker::createCategory(" + name + ", (" + keywords + "), " 
  144.                 + regex);
  145.         var cat = new CustomCategory(name, keywords, regex);
  146.         this.types[cat.id] = cat;
  147.  
  148.         
  149.         if(!noAppUpdate){
  150.             this.app.addCustomCategory(name, keywords, regex);
  151.             this.observer.notifyObservers(null, "boomtango-tracker-add", cat.id);
  152.         }
  153.         return cat.id;
  154.     },
  155.     importTracker: function(importFile){
  156.         var id = '????';
  157.         var update = false;
  158.         var file = Components.classes["@mozilla.org/file/directory_service;1"]
  159.                             .getService(Components.interfaces.nsIProperties)
  160.                             .get("ProfD", Components.interfaces.nsIFile);
  161.         file.append("boomtango");
  162.         file.append("trackers");
  163.         var name = importFile.leafName;
  164.         this.app.log("tracker::importTracker (" + name + ")");
  165.         try {
  166.             var file2 = Components.classes["@mozilla.org/file/directory_service;1"]
  167.                             .getService(Components.interfaces.nsIProperties)
  168.                             .get("ProfD", Components.interfaces.nsIFile);
  169.             file2.append("boomtango");
  170.             file2.append("trackers");
  171.             file2.append(name);
  172.             if(file2.exists()){
  173.                 update = true;
  174.                 try {
  175.                     file2.remove(false);
  176.                 } catch(e){
  177.                     this.app.log("error deleting custom js: " + e.message);
  178.                 }
  179.             }
  180.              
  181.             importFile.copyTo(file, name);
  182.             file.append(name);
  183.             var ios = Components.classes["@mozilla.org/network/io-service;1"].
  184.                             getService(Components.interfaces.nsIIOService);
  185.             var jsregex =  /^(.*)\.bct$/;
  186.             var results = jsregex.exec(name);
  187.             if(results){
  188.                 var loader = Components.classes
  189.                         ["@mozilla.org/moz/jssubscript-loader;1"]
  190.                         .getService(Components.interfaces.mozIJSSubScriptLoader);
  191.                 var jsurl =  ios.newFileURI(file).spec;
  192.                 var newtype = {};
  193.                 loader.loadSubScript(jsurl, newtype);
  194.                 if(newtype.module){
  195.                     var id = "import." + results[1];
  196.                     this.types[id] = newtype.module;
  197.                     if(!newtype.module.name_plural){
  198.                         newtype.module.name_plural = newtype.module.name;
  199.                     }
  200.                     this.types[id].id = id;
  201.                     this.addMemberFuncs(this.types[id]);
  202.                     if(newtype.module.init){
  203.                         newtype.module.init(null);
  204.                     }
  205.                     this.observer.notifyObservers(null, "boomtango-tracker-add", id);
  206.                 }
  207.             }
  208.         } catch(e){
  209.            this.app.log("error importing custom js: " + e.message);
  210.         }
  211.         return update ?  '????' : id;
  212.     },
  213.     addMemberFuncs: function(module){
  214.         var app = this.app;
  215.         var id = module.id;
  216.         var getDataFunc = function(){
  217.             return app.getTrackerData(id);
  218.         };
  219.         var setDataFunc = function(data){
  220.             app.setTrackerData(id, data);
  221.         };
  222.         var log = function(s){
  223.             app.log(s);
  224.         };
  225.         var debug = function(s){
  226.             app.debug(s);
  227.         };
  228.  
  229.         module.getData = getDataFunc;
  230.         module.setData = setDataFunc;
  231.         module.app = app;
  232.         module.log = log;
  233.         module.debug = debug;
  234.     },
  235.     deleteTracker: function(type){
  236.         var a = type.split('.');
  237.         if(!a.length){
  238.             return;
  239.         }
  240.  
  241.         switch(a[0]){
  242.             case 'custom':
  243.                 var name = decodeURIComponent(a[1]);
  244.                 this.app.removeCustomCategory(name);
  245.                 var types = this.types;
  246.                 delete types[type];
  247.                 this.observer.notifyObservers(null, "boomtango-tracker-remove", type);
  248.                 break;
  249.             case 'import':
  250.                 var file = Components.classes["@mozilla.org/file/directory_service;1"]
  251.                                     .getService(Components.interfaces.nsIProperties)
  252.                                     .get("ProfD", Components.interfaces.nsIFile);
  253.                 file.append("boomtango");
  254.                 file.append("trackers");
  255.                 file.append(a[1] + ".bct");
  256.                 try {
  257.                     file.remove(false);
  258.                 } catch(e){
  259.                     this.app.log("error deleting custom js: " + e.message);
  260.                 }
  261.  
  262.                 var types = this.types;
  263.                 delete types[type];
  264.                 this.observer.notifyObservers(null, "boomtango-tracker-remove", type);
  265.                 break;
  266.         }
  267.  
  268.     },
  269.     init: function(app){
  270.         this.app = app;
  271.         this.storage = app.storage;
  272.         this.purgatory = {};
  273.         this.app.debug("tracker::init");
  274.  
  275.         var types = this.types;
  276.         var len = EXTERNAL_TRACKERS.length;
  277.         while(len--){
  278.             this.app.debug("Adding " + EXTERNAL_TRACKERS[len]);
  279.             Components.utils.import("resource://boomtango/trackers/" + EXTERNAL_TRACKERS[len]  + ".js", types);
  280.         }
  281.         this.app.debug("Internal Trackers Loaded");
  282.         for(var x in types){
  283.             if(types.hasOwnProperty(x) && types[x].isInternal){
  284.                 types[x].app = this.app;
  285.                 types[x].name = this.app.getString("type.name." + x);
  286.                 types[x].name_plural = this.app.getString("type.name_plural." + x);
  287.                 types[x].description = this.app.getString("type.description." + x);
  288.             }
  289.         }
  290.         this.loadCategoryTrackers();
  291.         this.loadCustomTrackers();
  292.         var history = this.cc["@mozilla.org/browser/nav-history-service;1"].
  293.             getService(this.ci.nsINavHistoryService);
  294.         
  295.         this.observer = this.cc["@mozilla.org/observer-service;1"].
  296.                     getService(this.ci.nsIObserverService);
  297.  
  298.         history.addObserver(this, false);
  299.         this.init = function(){};
  300.     },
  301.     importBrowserHistoryDone: function(callback){
  302.         this.app.log("tracker:importDone");
  303.         this._inImport = false;
  304.         if(this.importData){
  305.             this.importData.containerOpen = false;
  306.             delete this.importData;
  307.         }
  308.     },
  309.     importBrowserHistoryDoFTS: function(callback, loadFunc){
  310.         this.app.log("tracker:importFTS(" + this.importDataCtr + ")");
  311.         var storage = this.storage;
  312.         var cont = this.importData;
  313.         var len = this.importDataLength;
  314.         if(this.importDataCtr >= len){
  315.             callback(this.importDataCtr, len, false);
  316.             return;
  317.         }
  318.         var node = cont.getChild(this.importDataCtr);
  319.         var title = node.title;
  320.         var url = node.uri;
  321.         var starttime = Math.round(node.time / 1000);
  322.         var self = this;
  323.  
  324.         loadFunc(url, function(doc, thumb){
  325.             var content = "";
  326.             if(doc){
  327.                 content = doc.body ? doc.body.textContent  : "";
  328.             }
  329.             var tracks = [
  330.                 // add a general tab
  331.                 {
  332.                     type: "web",
  333.                     title: node.title,
  334.                     textContent: "",
  335.                     preview:  thumb.toString()
  336.                 }
  337.             ];
  338.             var types = this.types;
  339.             for( var x in types){
  340.                 if(types.hasOwnProperty(x)){
  341.                     var helper = types[x];
  342.                     if(helper.onLoadTracks && this.app.getTrackerEnabled(x)){
  343.                         var a = helper.onLoadTracks(url, doc);
  344.                         for(var x = 0; x < a.length; x++){
  345.                             tracks.push(a[x]);
  346.                         }
  347.                     }
  348.                     if(helper.onUnloadTracks){
  349.                         var a = helper.onUnloadTrack(url, doc);
  350.                         for(var x = 0; x < a.length; x++){
  351.                             tracks.push(a[x]);
  352.                         }
  353.                     }
  354.                 }
  355.             }
  356.  
  357.             var len = tracks.length;
  358.             for(var x = 0; x < len; x++){
  359.                 var t = tracks[x];
  360.                 if(t.content){
  361.                     content += " " + t.content;
  362.                 } else if(t.type == "shopping" && t.redirect_url){
  363.                     url = t.redirect_url;
  364.                 }
  365.             }
  366.             var ftsrowid = storage.addFTS(title, content, starttime,
  367.                     -1, url);
  368.             storage.addTrackers(
  369.                 url, 
  370.                 title, 
  371.                 ftsrowid, 
  372.                 starttime, starttime, 
  373.                 tracks
  374.             );
  375.  
  376.             self.importDataCtr++;
  377.             if(self.importDataCtr >= len){
  378.                 self.importBrowserHistoryDone();
  379.                 callback(self.importDataCtr, len, false);
  380.             } else {
  381.                 callback(self.importDataCtr, len, true);
  382.             }
  383.         });
  384.     },
  385.     importBrowserHistoryDo: function(callback){
  386.         this.app.log("tracker::importBrowserHistoryDo");
  387.         var storage = this.storage;
  388.         var cont = this.importData;
  389.         var len = this.importDataLength;
  390.         var d = Date.now();
  391.         for (; this.importDataCtr < len && Date.now() < d + 100; this.importDataCtr++) {
  392.             if(!callback(this.importDataCtr, len)){
  393.                 this.importBrowserHistoryDone();
  394.                 return;
  395.             }
  396.             var node = cont.getChild(this.importDataCtr);
  397.             var title = node.title;
  398.             var url = node.uri;
  399.             var starttime = Math.round(node.time / 1000);
  400.             var content = "";
  401.             var tracks = [
  402.                 // add a general tab
  403.                 {
  404.                     type: "web",
  405.                     title: node.title,
  406.                     textContent: "",
  407.                     preview:  ""
  408.                 }
  409.             ];
  410.             var types = this.types;
  411.             for( var x in types){
  412.                 if(types.hasOwnProperty(x)){
  413.                     var helper = types[x];
  414.                     if(helper.onLoadTracks && this.app.getTrackerEnabled(x)){
  415.                         var a = helper.onLoadTracks(url);
  416.                         for(var x = 0; x < a.length; x++){
  417.                             tracks.push(a[x]);
  418.                         }
  419.                     }
  420.                     if(helper.onUnloadTracks){
  421.                         var a = helper.onUnloadTracks(url);
  422.                         for(var x = 0; x < a.length; x++){
  423.                             tracks.push(a[x]);
  424.                         }
  425.                     }
  426.                 }
  427.             }
  428.             var tracklen = tracks.length;
  429.             for(var x = 0; x < tracklen; x++){
  430.                 var t = tracks[x];
  431.                 if(t.content){
  432.                     content += " " + t.content;
  433.                 } else if(t.type == "shopping" && t.redirect_url){
  434.                     url = t.redirect_url;
  435.                 }
  436.             }
  437.             var ftsrowid = storage.addFTS(title, content, starttime,
  438.                     -1, url);
  439.             storage.addTrackers(
  440.                 url, 
  441.                 title, 
  442.                 ftsrowid, 
  443.                 starttime, starttime, 
  444.                 tracks
  445.             );
  446.         }
  447.         if(this.importDataCtr >= len){
  448.             this.importBrowserHistoryDone();
  449.             return false;
  450.         } else {
  451.             return true;
  452.         }
  453.     },
  454.     importBrowserHistoryStart: function(){
  455.         this.app.debug("tracker::importBrowserHistory");
  456.         this._inImport = true;
  457.         var historyService = 
  458.             Components.classes["@mozilla.org/browser/nav-history-service;1"]
  459.             .getService(Components.interfaces.nsINavHistoryService);
  460.  
  461.         var options = historyService.getNewQueryOptions();
  462.  
  463.         options.queryType = options.QUERY_TYPE_HISTORY;
  464.         options.resultType = options.RESULTS_AS_VISIT;
  465.         options.sortingMode = options.SORT_BY_DATE_ASCENDING;
  466.  
  467.  
  468.         var query = historyService.getNewQuery();
  469.         var result = historyService.executeQuery(query, options);
  470.         this.importData = result.root;
  471.         this.importData.containerOpen = true;
  472.         this.importDataCtr = 0;
  473.         this.importDataLength = this.importData.childCount;
  474.                 
  475.         this.app.debug("import count = " + this.importData.childCount );
  476.     },
  477.     onPageLoad: function(browserID, doc, thumb, referrer){
  478.         var url = doc.location.href;
  479.         this.app.log("tracker:onPageLoad (" + url + ")");
  480.         var startTime = Date.now();
  481.  
  482.         var tracks = [
  483.             // add a general tab
  484.             {
  485.                 type: "web",
  486.                 title: doc.title,
  487.                 preview:  thumb
  488.             }
  489.         ];
  490.         var content = "";
  491.         if(doc.body){
  492.             var tmpBody = doc.body.cloneNode(true);
  493.             Array.prototype.slice.call(
  494.                 tmpBody.getElementsByTagName('script')
  495.             ).forEach(function(i) { i.parentNode.removeChild(i);});
  496.  
  497.             content = [tmpBody.textContent.replace(/\s+/g,' '),doc.title].join(' ');
  498.             delete tmpBody;
  499.         }
  500.  
  501.         var types = this.types;
  502.         var trkctr = 0;
  503.         for( var x in types){
  504.             if(types.hasOwnProperty(x)){
  505.                 var helper = types[x];
  506.                 if(helper.onLoadTracks && this.app.getTrackerEnabled(x)){
  507.                     trkctr++;
  508.                     var a = helper.onLoadTracks(url, doc, referrer, content);
  509.                     for(var x = 0; x < a.length; x++){
  510.                         tracks.push(a[x]);
  511.                     }
  512.                 }
  513.             }
  514.         }
  515.  
  516.         var len = tracks.length;
  517.         for(var x = 0; x < len; x++){
  518.             var t = tracks[x];
  519.             if(t.content){
  520.                 content += " " + t.content;
  521.             } else if(t.type == "shopping" && t.redirect_url){
  522.                 url = t.redirect_url;
  523.             }
  524.         }
  525.         var data = {
  526.             url: url,
  527.             browserID: browserID,
  528.             tracks: [],
  529.             title: doc.title,
  530.             ftsrowid: this.storage.addFTS(doc.title, content, startTime, -1, url),
  531.             startTime: startTime,
  532.             endTime: -1,
  533.             inSync: true
  534.         };
  535.         this.purgatory[browserID + "-" + url] = data;
  536.         this.storage.addTrackers(url, data.title, data.ftsrowid, 
  537.                 data.startTime, -1, tracks);
  538.         this.observer.notifyObservers(null, "boomtango-visit-add",
  539.                 data.ftsrowid.toString());
  540.  
  541.         this.app.log("End Page Load: " + trkctr + ", " + (Date.now() - startTime));
  542.     },
  543.     onPageUnload: function(browserID, doc){
  544.         var url = doc.location.href;
  545.         var endTime = Date.now();
  546.  
  547.         var tracks = [];
  548.  
  549.         var types = this.types;
  550.         for( var x in types){
  551.             if(types.hasOwnProperty(x)){
  552.                 var helper = types[x];
  553.                 if(helper.onUnloadTracks && this.app.getTrackerEnabled(x)){
  554.                     var a = helper.onUnloadTracks(url, doc);
  555.                     for(var x = 0; x < a.length; x++){
  556.                         tracks.push(a[x]);
  557.                     }
  558.                 }
  559.             }
  560.         }
  561.  
  562.         var data = this.purgatory[browserID + "-" + url];
  563.         if(data){
  564.             data.endTime = endTime;
  565.             this.storage.updateTrackerEnd(url, endTime, data.ftsrowid);
  566.             this.storage.addTrackers(url, data.title, data.ftsrowid, 
  567.                             data.startTime, data.endTime, tracks);
  568.             this.observer.notifyObservers(null, "boomtango-visit-change",data.ftsrowid.toString());
  569.             
  570.             this.purgatory[browserID + "-" + url] = null;
  571.         }
  572.     },
  573.     QueryInterface: function(iid) {  
  574.         if (iid.equals(this.ci.nsINavHistoryObserver) ||  
  575.                iid.equals(this.ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS) ||  
  576.                iid.equals(this.ci.nsISupports)) {  
  577.             return this;  
  578.         } 
  579.         throw Components.result.NS_ERROR_NO_INTERFACE;  
  580.     },
  581.  
  582.     //nsINavHistoryObserver
  583.     onBeforeDeleteURI: function(){},
  584.     onDeleteURI: function(uri){
  585.         this.storage.deleteTrackersForURL(uri.spec);
  586.         this.observer.notifyObservers(null, "boomtango-datachange",
  587.                 "delete");
  588.     },
  589.     onClearHistory: function(){},
  590.     onBeginUpdateBatch: function(){},
  591.     onEndUpdateBatch: function(){},
  592.     onPageChanged: function(){},
  593.     onPageExpired: function(){},
  594.     onTitleChanged: function(){},
  595.     onVisit: function(uri){
  596.     }
  597. };
  598.  
  599.